/**
 * @file cache promise缓存
 */

type Save = (data: any, expireTime: number) => void;

type NotSave = (data: any) => void;

type CacheFn = (save: Save, notSave: NotSave) => void

const cache = new Map();

const Timeout = 5 * 1000;

export function extendPromiseCache() {
    // @ts-ignore
    Promise.cache = function(key: number | string, fn: CacheFn): Promise<any> {
        const cacheKey = typeof key === 'function' ? '' : key;
        const cacheFn = typeof key === 'function' ? key : fn;

        if (!cacheKey || ['number', 'string'].indexOf(typeof cacheKey) < 0) {
            throw new Error('Promise.cache must has effective cache key.')
        }

        if (typeof cacheFn !== 'function') {
            throw new Error('Promise.cache must has effective cache function.')
        }

        // @ts-ignore
        const _save = (resolve, reject) => {
            return (data: any, expireTime: number = Timeout) => {
                // @ts-ignore
                if (Object.prototype.toString.call(data) === '[object Error]') {
                    return reject(data);
                } else {
                    cache.set(key, {cacheData: data, cacheTime: Date.now() + expireTime});
                    return resolve(data);
                }
            }
        };

        // @ts-ignore
        const _notSave = (resolve, reject) => {
            return (data: any) => {
                if (cache.has(key)) {
                    cache.delete(key);
                }
                if (Object.prototype.toString.call(data) === '[object Error]') {
                    return reject(data);
                } else {
                    return resolve(data);
                }
            }
        };

        const promise = new Promise((resolve, reject) => {
            const {cacheData, cacheTime} = cache.get(cacheKey) || {cacheData: '', cacheTime: 0};
            if (cacheData && Date.now() <= cacheTime) {
                if (Object.prototype.toString.call(cacheData) === '[object Error]') {
                    return reject(cacheData);
                } else {
                    return resolve(cacheData);
                }
            }

            const notSave = _notSave(resolve, reject);
            const save = _save(resolve, reject);

            cacheFn(save, notSave);
        });

        return promise;
    };

    // @ts-ignore
    Promise.clearCache = function(key: string | number) {
        if (!key || ['number', 'string'].indexOf(typeof key) === -1) {
            throw new Error('Promise.clearCache must has effective cache key.')
        }
        if (cache.has(key)) {
            cache.delete(key);
            return Promise.resolve(true);
        } else {
            return Promise.resolve(false);
        }
    }
}
